//
// Created by jack on 2021/2/3.
//

#include "weilapb_defines.h"

#include <WL.Service.pb.h>
#include <WL.Session.pb.h>
#include <base64.h>
#include <pb_decode.h>
#include <stdio.h>
#include <stdlib.h>

#include "weilapb_datafuns.h"

#include "weilapb_commondec.h"
#include "weilapb_cryptutil.h"
#include "weilapb_error.h"
#include "weilapb_internal.h"
#include "weilapb_sessionmsg.h"

#define STR_TRIM_LEFT(str)                                                                                             \
    while ((PB_CHAR)(*str) == ' ')                                                                                     \
        str++;

#define STR_TRIM_RIGHT(str)                                                                                            \
    while ((PB_CHAR)(*(str + strlen(str) - 1)) == ' ')                                                                 \
        *(str + strlen(str) - 1) = 0;

PB_BOOL weilapb_parseTextExtMsgData(PB_CHAR* pExtText, PBExtTextData* pPbExtTextData) {
    PB_CHAR*  pDecryptedText    = PB_NULL;
    PB_UINT32 decryptedTextSize = 0;

    // 第一步先解密
    PB_UINT32 len = strlen(pExtText);
    if (len < 4) {
        len = 16;
    } else {
        len *= 2;
    }
    if (weilapb_decryptText(pExtText, len, &pDecryptedText, &decryptedTextSize) == 0) {
        // 第二步，定位内容
        if (strstr(pDecryptedText, TEXT_EXTEND_START)) {
            PB_CHAR* pParameter;
            PB_CHAR* pContent    = (pDecryptedText + strlen(TEXT_EXTEND_START));
            PB_CHAR* pContentEnd = strstr(pContent, TEXT_EXTEND_END);
            if (!pContentEnd) {
                // 未找到结尾定位，视为无效消息。
                goto Error;
            }

            *pContentEnd = 0;  // 清掉末尾

            pParameter = strstr(pContent, "?");
            // 定位是否有参数，如果有参数，// 说明是属于file或video或location类型
            // 无参数则是图片或command或文字类型

            if (pParameter) {
                *pParameter = 0;
                pParameter += 1;

                do {
                    PB_CHAR *pKey, *pValue, *pValueEnd;
                    pValue = strstr(pParameter, "=");
                    if (!pValue) {
                        break;
                    }

                    pKey    = pParameter;
                    *pValue = 0;  // 让pKey成为字符串
                    pValue  = pValue + 1;

                    pValueEnd = strstr(pValue, "&");
                    if (pValueEnd) {
                        *pValueEnd = 0;
                    }

                    pParameter = pValue + strlen(pValue) + 1;
                    STR_TRIM_LEFT(pKey);
                    STR_TRIM_RIGHT(pKey);

                    STR_TRIM_LEFT(pValue);
                    STR_TRIM_RIGHT(pValue);

                    if (!strcmp(pKey, SERVICE_TYPE_KEY)) {
                        if (!strcmp(pValue, SERVICE_TYPE_FILE)) {
                            pPbExtTextData->pExtData = (PBFileInfo*)PB_ALLOC(sizeof(PBFileInfo));
                            if (!pPbExtTextData->pExtData) {
                                goto Error;
                            }

                            pPbExtTextData->msgType     = IM_FILE_INFO_TYPE;
                            pPbExtTextData->extDataSize = sizeof(PBFileInfo);
                            if (strlen(pContent) < MAX_URL_STRING_LEN) {
                                strcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileIcon, pContent);
                            } else {
                                memcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileIcon,
                                       pContent,
                                       MAX_URL_STRING_LEN - 1);
                            }
                        } else if (!strcmp(pValue, SERVICE_TYPE_VIDEO)) {
                            pPbExtTextData->pExtData = (PBFileInfo*)PB_ALLOC(sizeof(PBFileInfo));
                            if (!pPbExtTextData->pExtData) {
                                goto Error;
                            }

                            pPbExtTextData->msgType     = IM_VIDEO_INFO_TYPE;
                            pPbExtTextData->extDataSize = sizeof(PBFileInfo);
                            if (strlen(pContent) < MAX_URL_STRING_LEN) {
                                strcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileIcon, pContent);
                            } else {
                                memcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileIcon,
                                       pContent,
                                       MAX_URL_STRING_LEN - 1);
                            }
                        } else if (!strcmp(pValue, SERVICE_TYPE_LOCATION)) {
                            pPbExtTextData->pExtData = (PBLocationInfoShared*)PB_ALLOC(sizeof(PBLocationInfoShared));
                            if (!pPbExtTextData->pExtData) {
                                goto Error;
                            }

                            pPbExtTextData->msgType     = IM_LOCATION_INFO_TYPE;
                            pPbExtTextData->extDataSize = sizeof(PBLocationInfoShared);
                            if (strlen(pContent) < MAX_URL_STRING_LEN) {
                                strcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationMapUrl, pContent);
                            } else {
                                memcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationMapUrl,
                                       pContent,
                                       MAX_URL_STRING_LEN - 1);
                            }
                        }
                    } else if (!strcmp(pKey, LOCATION_TYPE_KEY)) {
                        if (strlen(pValue) < MAX_SHORTEST_STRING_LEN) {
                            strcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationType, pValue);
                        } else {
                            memcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationType,
                                   pValue,
                                   MAX_SHORTEST_STRING_LEN - 1);
                        }
                    } else if (!strcmp(pKey, LATITUDE_KEY)) {
                        ((PBLocationInfoShared*)pPbExtTextData->pExtData)->latitude = atof(pValue);
                    } else if (!strcmp(pKey, LONGITUDE_KEY)) {
                        ((PBLocationInfoShared*)pPbExtTextData->pExtData)->longitude = atof(pValue);
                    } else if (!strcmp(pKey, LOCATION_NAME_KEY)) {
                        if (strlen(pValue) < MAX_SHORT_STRING_LEN) {
                            strcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationName, pValue);
                        } else {
                            memcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationName,
                                   pValue,
                                   MAX_SHORT_STRING_LEN - 1);
                        }
                    } else if (!strcmp(pKey, LOCATION_ADDRESS_KEY)) {
                        if (strlen(pValue) < MAX_NORMAL_STRING_LEN) {
                            strcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationAddr, pValue);
                        } else {
                            memcpy(((PBLocationInfoShared*)pPbExtTextData->pExtData)->locationAddr,
                                   pValue,
                                   MAX_NORMAL_STRING_LEN - 1);
                        }
                    } else if (!strcmp(pKey, FILE_URL_KEY)) {
                        if (strlen(pValue) < MAX_URL_STRING_LEN) {
                            strcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileUrl, pValue);
                        } else {
                            memcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileUrl, pValue, MAX_URL_STRING_LEN - 1);
                        }
                    } else if (!strcmp(pKey, FILE_SIZE_KEY)) {
                        ((PBFileInfo*)pPbExtTextData->pExtData)->fileSize = atoi(pValue);
                    } else if (!strcmp(pKey, FILE_NAME_KEY)) {
                        PB_UINT32 outputSize = (strlen(pValue) / 3 + 1) * 4;
                        PB_CHAR*  pFilename  = (PB_CHAR*)PB_ALLOC(outputSize);
                        if (pFilename) {
                            if (wl_base64_decode(pValue, strlen(pValue), pFilename, &outputSize) == 0) {
                                if (strlen(pFilename) < MAX_SHORT_STRING_LEN) {
                                    strcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileName, pFilename);
                                } else {
                                    memcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileName,
                                           pFilename,
                                           MAX_SHORT_STRING_LEN - 1);
                                }
                            } else {
                                strcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileName, "unknown_filename");
                            }

                            PB_FREE(pFilename);
                        } else {
                            strcpy(((PBFileInfo*)pPbExtTextData->pExtData)->fileName, "unknown_filename");
                        }
                    }
                } while (PB_TRUE);
            } else {
                pPbExtTextData->pExtData = PB_ALLOC(strlen(pContent) + 1);
                if (pPbExtTextData->pExtData) {
                    pPbExtTextData->msgType = IM_IMAGE_INFO_TYPE;
                    strcpy((PB_CHAR*)pPbExtTextData->pExtData, pContent);
                    pPbExtTextData->extDataSize = strlen(pContent) + 1;
                } else {
                    goto Error;
                }
            }
        } else if (strstr(pDecryptedText, CMD_EXTEND_START)) {
            PB_CHAR* pContent    = (pDecryptedText + strlen(CMD_EXTEND_START));
            PB_CHAR* pContentEnd = strstr(pContent, CMD_EXTEND_END);
            if (!pContentEnd) {
                // 未找到结尾定位，视为无效消息。
                goto Error;
            }

            *pContentEnd             = 0;  // 清掉末尾
            pPbExtTextData->pExtData = PB_ALLOC(strlen(pContent) + 1);
            if (pPbExtTextData->pExtData) {
                pPbExtTextData->msgType = IM_COMMAND_TYPE;
                strcpy((PB_CHAR*)pPbExtTextData->pExtData, pContent);
                pPbExtTextData->extDataSize = strlen(pContent) + 1;
            } else {
                goto Error;
            }
        } else {
            pPbExtTextData->pExtData = PB_ALLOC(strlen(pDecryptedText) + 1);
            if (pPbExtTextData->pExtData) {
                pPbExtTextData->msgType = IM_TEXT_MSG_TYPE;
                strcpy((PB_CHAR*)pPbExtTextData->pExtData, pDecryptedText);
                pPbExtTextData->extDataSize = strlen(pDecryptedText) + 1;
            } else {
                goto Error;
            }
        }

        if (pDecryptedText) {
            PB_FREE(pDecryptedText);
        }
        return PB_TRUE;
    } else {
        pPbExtTextData->pExtData    = PB_NULL;
        pPbExtTextData->msgType     = IM_UNKNOWN_TYPE;
        pPbExtTextData->extDataSize = 0;
    }

Error:
    weilapb_error(ERR_DECODE, "decode text extend failed");
    if (pDecryptedText != PB_NULL) {
        PB_FREE(pDecryptedText);
    }
    return PB_FALSE;
}

PB_BOOL
weilapb_decodeOpusData(PB_UINT8* pEncodedOpus, PB_UINT32 encodedSize, PBLinkList* pList, PB_UINT32* pFrameCount) {
    PB_UINT32 offset         = 0;
    PB_UINT8  flag           = 0;
    PB_UINT32 frameCount     = 0;
    PB_UINT32 sizeOfOpusData = 0;
    PB_BOOL   ret            = PB_TRUE;

    while (offset < encodedSize) {
        flag = pEncodedOpus[offset];
        offset += 1;

        if (flag & 0x80) {
            sizeOfOpusData = ((PB_UINT32)pEncodedOpus[offset] << 8 | pEncodedOpus[offset + 1]) + 1;
            offset += 2;
        } else {
            sizeOfOpusData = pEncodedOpus[offset] + 1;
            offset += 1;
        }

        if (pList) {
            PBLinkItem* pTemp;
            PBLinkItem* pItem = PB_ALLOC(sizeof(PBLinkItem) + sizeOfOpusData);

            if (pItem == PB_NULL) {
                weilapb_freePBLinkList(pList);
                break;
            }

            pItem->dataSize = sizeOfOpusData;
            pItem->data[0]  = flag;
            pItem->pNext    = PB_NULL;
            memcpy(&pItem->data[1], &pEncodedOpus[offset], sizeOfOpusData - 1);

            pTemp = pList->pLinks;
            if (pTemp == PB_NULL) {
                pList->pLinks = pItem;
            } else {
                while (pTemp->pNext) {
                    pTemp = pTemp->pNext;
                }
                pTemp->pNext = pItem;
            }
            pList->listCount++;
        }

        frameCount++;
        offset += (sizeOfOpusData - 1);
    }

    if (pFrameCount) {
        *pFrameCount = frameCount;
    }

    return ret;
}

PB_BOOL weilapb_parseMsgData(PBMsgData** ppMsgData, PBFieldData* pFieldDataList, WL_Session_MsgData* pSessionMsgData) {
    PBExtTextData pbExtTextData;
    PBMsgData*    pMsgData    = PB_NULL;
    PB_UINT32     contentSize = 0;
    PB_UINT32     msgType     = IM_UNKNOWN_TYPE;

    if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_AUDIO
        && pFieldDataList[MSGDATA_AUDIO_URL_FIELD].dataSize > 0) {
        msgType     = IM_AUDIO_URL_TYPE;
        contentSize = pFieldDataList[MSGDATA_AUDIO_URL_FIELD].dataSize;
    } else if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_AUDIO
               && pFieldDataList[MSGDATA_AUDIO_DATA_FIELD].dataSize > 0) {
        msgType     = IM_AUDIO_DATA_TYPE;
        contentSize = sizeof(PBLinkList);
    } else if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_SERVICE
               && pFieldDataList[MSGDATA_SERVICE_FIELD].dataSize > 0) {
        msgType     = IM_SERVICE_TYPE;
        contentSize = pFieldDataList[MSGDATA_SERVICE_FIELD].dataSize;
    } else if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_SWITCH
               && pFieldDataList[MSGDATA_SWITCH_DATA_FIELD].dataSize > 0) {
        msgType     = IM_SWITCH_DATA_TYPE;
        contentSize = pFieldDataList[MSGDATA_SWITCH_DATA_FIELD].dataSize;
    } else if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_PTT) {
        msgType     = IM_PTT_DATA_TYPE;
        contentSize = sizeof(PBPtt);
    } else if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_VOIP) {
        msgType     = IM_VOIP_DATA_TYPE;
        contentSize = sizeof(PBPtt);
    } else if (pSessionMsgData->msgType == WL_Session_MessageType_MSG_TYPE_TEXT
               && pFieldDataList[MSGDATA_TEXT_FIELD].dataSize > 0) {
        memset(&pbExtTextData, 0, sizeof(PBExtTextData));
        if (weilapb_parseTextExtMsgData(pFieldDataList[MSGDATA_TEXT_FIELD].pData, &pbExtTextData)) {
            msgType     = pbExtTextData.msgType;
            contentSize = pbExtTextData.extDataSize;
        } else {
            return PB_FALSE;
        }
    }

    if (msgType == IM_AUDIO_URL_TYPE || msgType == IM_SERVICE_TYPE || msgType == IM_SWITCH_DATA_TYPE
        || msgType == IM_TEXT_MSG_TYPE || msgType == IM_IMAGE_INFO_TYPE || msgType == IM_FILE_INFO_TYPE
        || msgType == IM_VIDEO_INFO_TYPE || msgType == IM_LOCATION_INFO_TYPE || msgType == IM_COMMAND_TYPE) {
        pMsgData = (PBMsgData*)PB_ALLOC(sizeof(PBMsgData) + contentSize);

    } else if (msgType == IM_PTT_DATA_TYPE || msgType == IM_VOIP_DATA_TYPE) {
        pMsgData = (PBMsgData*)PB_ALLOC(sizeof(PBMsgData) + sizeof(PBPtt));
    } else if (msgType == IM_AUDIO_DATA_TYPE) {
        pMsgData = (PBMsgData*)PB_ALLOC(sizeof(PBMsgData) + sizeof(PBLinkList));
    } else if (msgType == IM_UNKNOWN_TYPE) {
        pMsgData = (PBMsgData*)PB_ALLOC(sizeof(PBMsgData));
    }

    if (pMsgData) {
        pMsgData->msgType     = msgType;
        pMsgData->contentSize = contentSize;
        pMsgData->msgId       = pSessionMsgData->msgId;
        pMsgData->created     = pSessionMsgData->created;
        pMsgData->sessionType = pSessionMsgData->sessionType;
        pMsgData->sessionId   = pSessionMsgData->sessionId;
        pMsgData->senderId    = pSessionMsgData->senderId;

        if (pFieldDataList[MSGDATA_TAG_FIELD].dataSize > 0) {
            if (pFieldDataList[MSGDATA_TAG_FIELD].dataSize >= sizeof(pMsgData->senderName)) {
                memcpy(pMsgData->senderName,
                   pFieldDataList[MSGDATA_TAG_FIELD].pData,
                   sizeof(pMsgData->senderName)-1);
                   pMsgData->senderName[sizeof(pMsgData->senderName)-1] = 0;
            } else {
                memcpy(pMsgData->senderName,
                   pFieldDataList[MSGDATA_TAG_FIELD].pData,
                   pFieldDataList[MSGDATA_TAG_FIELD].dataSize);
                pMsgData->senderName[pFieldDataList[MSGDATA_TAG_FIELD].dataSize] = 0;
            }
        }

        switch (msgType) {
            case IM_COMMAND_TYPE:
            case IM_FILE_INFO_TYPE:
            case IM_VIDEO_INFO_TYPE:
            case IM_IMAGE_INFO_TYPE:
            case IM_LOCATION_INFO_TYPE:
            case IM_TEXT_MSG_TYPE: {
                memcpy(pMsgData->content, pbExtTextData.pExtData, pbExtTextData.extDataSize);
                PB_FREE(pbExtTextData.pExtData);
            } break;

            case IM_AUDIO_DATA_TYPE: {
                PBLinkList* pList = (PBLinkList*)pMsgData->content;
                if (!weilapb_decodeOpusData(pFieldDataList[MSGDATA_AUDIO_DATA_FIELD].pData,
                                            pFieldDataList[MSGDATA_AUDIO_DATA_FIELD].dataSize,
                                            pList,
                                            PB_NULL)) {
                    pList->listCount = 0;
                }
            } break;
            case IM_SWITCH_DATA_TYPE: {
                memcpy(pMsgData->content, pFieldDataList[MSGDATA_SWITCH_DATA_FIELD].pData, contentSize);
            } break;
            case IM_SERVICE_TYPE: {
                memcpy(pMsgData->content, pFieldDataList[MSGDATA_SERVICE_FIELD].pData, contentSize);
            } break;
            case IM_AUDIO_URL_TYPE: {
                memcpy(pMsgData->content, pFieldDataList[MSGDATA_AUDIO_URL_FIELD].pData, contentSize);
            } break;
            case IM_PTT_DATA_TYPE:
            case IM_VOIP_DATA_TYPE: {
                PBPtt* pPbPtt      = (PBPtt*)pMsgData->content;
                pPbPtt->seq        = pSessionMsgData->pttData.seq;
                pPbPtt->marker     = pSessionMsgData->pttData.marker;
                pPbPtt->packageSeq = pSessionMsgData->pttData.packageSeq;
                pPbPtt->source     = pSessionMsgData->pttData.source;

                if (pSessionMsgData->has_pttData) {
                    if (pSessionMsgData->pttData.opusData.has_frameCount) {
                        pPbPtt->frameCount = pSessionMsgData->pttData.opusData.frameCount;
                    }

                    if (!weilapb_decodeOpusData(pFieldDataList[MSGDATA_OPUS_DATA_FIELD].pData,
                                                pFieldDataList[MSGDATA_OPUS_DATA_FIELD].dataSize,
                                                &pPbPtt->frameList,
                                                PB_NULL)) {
                        pPbPtt->frameCount = 0;
                    }
                } else {
                    pPbPtt->frameCount = 0;
                }
            } break;
        }

        *ppMsgData = pMsgData;
        return PB_TRUE;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate msgData");
    }

    return PB_FALSE;
}

PB_BOOL weilapb_prepareDecodeMsgdata(PBFieldData* pFieldDataList, WL_Session_MsgData* pMsgData) {
    memset(pFieldDataList, 0, sizeof(PBFieldData) * MSGDATA_FIELD_COUNT);

    // tag
    pFieldDataList[MSGDATA_TAG_FIELD].pData = (PB_CHAR*)PB_ALLOC(MAX_SHORT_STRING_LEN);
    if (pFieldDataList[MSGDATA_TAG_FIELD].pData == PB_NULL) {
        return PB_FALSE;
    }
    weilapb_initDecDataItem(&pFieldDataList[MSGDATA_TAG_FIELD],
                            PB_DATA_TYPE_DYNAMIC_STRING,
                            MAX_SHORT_STRING_LEN,
                            pFieldDataList[MSGDATA_TAG_FIELD].pData);
    weilapb_setDecCallback(&pFieldDataList[MSGDATA_TAG_FIELD], &pMsgData->tag, PB_NULL);

    if (pMsgData->msgType == WL_Session_MessageType_MSG_TYPE_TEXT) {
        // 文字,先用一般长度，如果不足，会自动补足
        pFieldDataList[MSGDATA_TEXT_FIELD].pData = (PB_CHAR*)PB_ALLOC(MAX_LONG_STRING_LEN);
        if (pFieldDataList[MSGDATA_TEXT_FIELD].pData == PB_NULL) {
            return PB_FALSE;
        }
        weilapb_initDecDataItem(&pFieldDataList[MSGDATA_TEXT_FIELD],
                                PB_DATA_TYPE_DYNAMIC_STRING,
                                MAX_LONG_STRING_LEN,
                                pFieldDataList[MSGDATA_TEXT_FIELD].pData);
        weilapb_setDecCallback(&pFieldDataList[MSGDATA_TEXT_FIELD], &pMsgData->textData, PB_NULL);
    } else if (pMsgData->msgType == WL_Session_MessageType_MSG_TYPE_AUDIO) {
        // audio, byte也是动态申请
        pFieldDataList[MSGDATA_AUDIO_DATA_FIELD].pData = (PB_UINT8*)PB_ALLOC(MAX_LONG_STRING_LEN);
        if (pFieldDataList[MSGDATA_AUDIO_DATA_FIELD].pData == PB_NULL) {
            return PB_FALSE;
        }
        weilapb_initDecDataItem(&pFieldDataList[MSGDATA_AUDIO_DATA_FIELD],
                                PB_DATA_TYPE_DYNAMIC_STRING,
                                MAX_LONG_STRING_LEN,
                                pFieldDataList[MSGDATA_AUDIO_DATA_FIELD].pData);
        weilapb_setDecCallback(&pFieldDataList[MSGDATA_AUDIO_DATA_FIELD], &pMsgData->audioData.payload, PB_NULL);

        // audio url
        pFieldDataList[MSGDATA_AUDIO_URL_FIELD].pData = (PB_UINT8*)PB_ALLOC(MAX_URL_STRING_LEN);
        if (pFieldDataList[MSGDATA_AUDIO_URL_FIELD].pData == PB_NULL) {
            return PB_FALSE;
        }
        weilapb_initDecDataItem(&pFieldDataList[MSGDATA_AUDIO_URL_FIELD],
                                PB_DATA_TYPE_DYNAMIC_STRING,
                                MAX_URL_STRING_LEN,
                                pFieldDataList[MSGDATA_AUDIO_URL_FIELD].pData);
        weilapb_setDecCallback(&pFieldDataList[MSGDATA_AUDIO_URL_FIELD], &pMsgData->audioData.url, PB_NULL);
    } else if (pMsgData->msgType == WL_Session_MessageType_MSG_TYPE_PTT
               || pMsgData->msgType == WL_Session_MessageType_MSG_TYPE_VOIP) {
        // opus data
        pFieldDataList[MSGDATA_OPUS_DATA_FIELD].pData = (PB_UINT8*)PB_ALLOC(MAX_LONG_STRING_LEN);
        if (pFieldDataList[MSGDATA_OPUS_DATA_FIELD].pData == PB_NULL) {
            return PB_FALSE;
        }
        weilapb_initDecDataItem(&pFieldDataList[MSGDATA_OPUS_DATA_FIELD],
                                PB_DATA_TYPE_DYNAMIC_STRING,
                                MAX_LONG_STRING_LEN,
                                pFieldDataList[MSGDATA_OPUS_DATA_FIELD].pData);
        weilapb_setDecCallback(&pFieldDataList[MSGDATA_OPUS_DATA_FIELD], &pMsgData->pttData.opusData.payload, PB_NULL);
    } else if (pMsgData->msgType == WL_Session_MessageType_MSG_TYPE_SWITCH) {
        // switch data
        pFieldDataList[MSGDATA_SWITCH_DATA_FIELD].pData = (PB_UINT8*)PB_ALLOC(MAX_LONG_STRING_LEN);
        if (pFieldDataList[MSGDATA_SWITCH_DATA_FIELD].pData == PB_NULL) {
            return PB_FALSE;
        }
        weilapb_initDecDataItem(&pFieldDataList[MSGDATA_SWITCH_DATA_FIELD],
                                PB_DATA_TYPE_DYNAMIC_STRING,
                                MAX_LONG_STRING_LEN,
                                pFieldDataList[MSGDATA_SWITCH_DATA_FIELD].pData);
        weilapb_setDecCallback(&pFieldDataList[MSGDATA_SWITCH_DATA_FIELD], &pMsgData->switchData.serviceData, PB_NULL);
    } else if (pMsgData->msgType == WL_Session_MessageType_MSG_TYPE_SERVICE) {
        // service data
        pFieldDataList[MSGDATA_SERVICE_FIELD].pData = (PB_UINT8*)PB_ALLOC(MAX_LONG_STRING_LEN);
        if (pFieldDataList[MSGDATA_SERVICE_FIELD].pData == PB_NULL) {
            return PB_FALSE;
        }
        weilapb_initDecDataItem(&pFieldDataList[MSGDATA_SERVICE_FIELD],
                                PB_DATA_TYPE_DYNAMIC_STRING,
                                MAX_LONG_STRING_LEN,
                                pFieldDataList[MSGDATA_SERVICE_FIELD].pData);
        weilapb_setDecCallback(&pFieldDataList[MSGDATA_SERVICE_FIELD], &pMsgData->serviceData, PB_NULL);
    }

    return PB_TRUE;
}

PBWeilaMsg* weilapb_decodeMsgDataNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_NULL;
    PBFieldData               msgDataItemFields[MSGDATA_FIELD_COUNT];
    PBWeilaMsg                weilaMsg;
    PB_INT32                  i;

    istream = pb_istream_from_buffer(pData, dataSize);

    if (!pb_decode(&istream, WL_Service_ServiceMessage_fields, &serviceMessage)) {
        weilapb_error(ERR_DECODE, "decode weila message failed");
        return PB_NULL;
    }

    if (!weilapb_prepareDecodeMsgdata(msgDataItemFields, &serviceMessage.sessionMessage.ntfMsg.msgData)) {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
        return PB_NULL;
    }

    memset(&weilaMsg, 0, sizeof(PBWeilaMsg));
    istream = pb_istream_from_buffer(pData, dataSize);
    if (weilapb_decodeWeilaMsg(&weilaMsg, &istream, &serviceMessage)) {
        if (serviceMessage.serviceHead.resultCode == 0) {
            PBMsgData* pPbMsgData = PB_NULL;
            if (weilapb_parseMsgData(&pPbMsgData, msgDataItemFields, &(serviceMessage.sessionMessage.ntfMsg.msgData))) {
                if (serviceMessage.sessionMessage.ntfMsg.has_onlineMemberCount) {
                    pPbMsgData->onlineCount = serviceMessage.sessionMessage.ntfMsg.onlineMemberCount;
                }

                pWeilaMsg = (PBWeilaMsg*)PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBMsgData) + pPbMsgData->contentSize);
                if (pWeilaMsg) {
                    memcpy(pWeilaMsg, &weilaMsg, sizeof(PBWeilaMsg));
                    for (i = 0; i < MSGDATA_FIELD_COUNT; i++) {
                        if (msgDataItemFields[i].pData) {
                            PB_FREE(msgDataItemFields[i].pData);
                        }
                    }
                    memcpy((PBMsgData*)(pWeilaMsg->pContent), pPbMsgData, sizeof(PBMsgData) + pPbMsgData->contentSize);
                    pWeilaMsg->contentSize = sizeof(PBMsgData) + pPbMsgData->contentSize;
                    if (pPbMsgData->msgType == IM_AUDIO_DATA_TYPE) {
                        PBLinkList* pLink = (PBLinkList*)((PBMsgData*)(pWeilaMsg->pContent))->content;
                        weilapb_addRecyclePointer(pWeilaMsg, RECYCLE_PTR_LINK, pLink);
                    } else if (pPbMsgData->msgType == IM_PTT_DATA_TYPE || pPbMsgData->msgType == IM_VOIP_DATA_TYPE) {
                        PBPtt* pPbPtt = (PBPtt*)((PBMsgData*)(pWeilaMsg->pContent))->content;
                        weilapb_addRecyclePointer(pWeilaMsg, RECYCLE_PTR_LINK, &pPbPtt->frameList);
                    }
                    PB_FREE(pPbMsgData);
                    pPbMsgData = PB_NULL;
                    return pWeilaMsg;
                } else {
                    if (pPbMsgData->msgType == IM_AUDIO_DATA_TYPE) {
                        weilapb_freePBLinkList((PBLinkList*)pPbMsgData->content);
                    } else if (pPbMsgData->msgType == IM_PTT_DATA_TYPE || pPbMsgData->msgType == IM_VOIP_DATA_TYPE) {
                        PBPtt* pPbPtt = (PBPtt*)pPbMsgData->content;
                        weilapb_freePBLinkList(&pPbPtt->frameList);
                    }

                    weilapb_error(ERR_OUT_OF_MEMORY, "allocate new weilaMsg failed");
                    PB_FREE(pPbMsgData);
                    pPbMsgData = PB_NULL;
                    pWeilaMsg  = PB_NULL;
                }
            } else {
                if (pPbMsgData) {
                    if (pPbMsgData->msgType == IM_AUDIO_DATA_TYPE) {
                        weilapb_freePBLinkList((PBLinkList*)pPbMsgData->content);
                    } else if (pPbMsgData->msgType == IM_PTT_DATA_TYPE || pPbMsgData->msgType == IM_VOIP_DATA_TYPE) {
                        PBPtt* pPbPtt = (PBPtt*)pPbMsgData->content;
                        weilapb_freePBLinkList(&pPbPtt->frameList);
                    }

                    PB_FREE(pPbMsgData);
                }
            }
        } else {
            pWeilaMsg = (PBWeilaMsg*)PB_ALLOC(sizeof(PBWeilaMsg));
            if (pWeilaMsg) {
                memcpy(pWeilaMsg, &weilaMsg, sizeof(PBWeilaMsg));
            } else {
                pWeilaMsg = PB_NULL;
                weilapb_error(ERR_OUT_OF_MEMORY, "allocate new weilaMsg failed");
            }
        }
    } else {
        weilapb_error(ERR_DECODE, "decode msg data fail");
        WL_LOG("decode msg data fail:%s\n", istream.errmsg);
        pWeilaMsg = PB_NULL;
    }

    for (i = 0; i < MSGDATA_FIELD_COUNT; i++) {
        if (msgDataItemFields[i].pData) {
            PB_FREE(msgDataItemFields[i].pData);
        }
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeMsgDataRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBSendMsgRsp));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        pWeilaMsg->contentSize = sizeof(PBSendMsgRsp);
        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.serviceHead.resultCode == 0) {
                PBSendMsgRsp* pSendMsgRsp = (PBSendMsgRsp*)pWeilaMsg->pContent;
                if (serviceMessage.sessionMessage.rspMsg.has_msgId) {
                    pSendMsgRsp->msgId = serviceMessage.sessionMessage.rspMsg.msgId;
                }
                if (serviceMessage.sessionMessage.rspMsg.has_created) {
                    pSendMsgRsp->createdTimestamp = serviceMessage.sessionMessage.rspMsg.created;
                }
                if (serviceMessage.sessionMessage.rspMsg.has_remoteStatus) {
                    pSendMsgRsp->remoteOffline = serviceMessage.sessionMessage.rspMsg.remoteStatus;
                }
                if (serviceMessage.sessionMessage.rspMsg.has_onlineMemberCount) {
                    pSendMsgRsp->onlineCount = serviceMessage.sessionMessage.rspMsg.onlineMemberCount;
                }
            }

            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

void* weilapb_decodeSubMsgData(pb_istream_t* stream, const pb_field_t* field) {
    WL_Session_MsgData wlSessionMsgData = WL_Session_MsgData_init_zero;
    PBFieldData        msgDataItemFields[MSGDATA_FIELD_COUNT];
    PBMsgData*         pPbMsgData  = PB_NULL;
    PBLinkItem*        pPbLinkItem = PB_NULL;
    PB_INT32           i;
    pb_istream_t       preStream = pb_istream_from_buffer(stream->state, stream->bytes_left);

    if (pb_decode(&preStream, WL_Session_MsgData_fields, &wlSessionMsgData)) {
        if (weilapb_prepareDecodeMsgdata(msgDataItemFields, &wlSessionMsgData)) {
            if (pb_decode(stream, WL_Session_MsgData_fields, &wlSessionMsgData)) {
                if (weilapb_parseMsgData(&pPbMsgData, msgDataItemFields, &wlSessionMsgData)) {
                    pPbLinkItem =
                        (PBLinkItem*)PB_ALLOC(sizeof(PBLinkItem) + sizeof(PBMsgData) + pPbMsgData->contentSize);
                    if (pPbLinkItem) {
                        pPbLinkItem->dataSize = sizeof(PBMsgData) + pPbMsgData->contentSize;
                        memcpy(pPbLinkItem->data, pPbMsgData, pPbLinkItem->dataSize);
                    }
                    PB_FREE(pPbMsgData);
                } else {
                    if (pPbMsgData->msgType == IM_AUDIO_DATA_TYPE) {
                        weilapb_freePBLinkList((PBLinkList*)pPbMsgData->content);
                    } else if (pPbMsgData->msgType == IM_PTT_DATA_TYPE || pPbMsgData->msgType == IM_VOIP_DATA_TYPE) {
                        PBPtt* pPbPtt = (PBPtt*)pPbMsgData->content;
                        weilapb_freePBLinkList(&pPbPtt->frameList);
                    }
                }
            }

            for (i = 0; i < MSGDATA_FIELD_COUNT; i++) {
                if (msgDataItemFields[i].pData) {
                    PB_FREE(msgDataItemFields[i].pData);
                }
            }
        }
    }

    return pPbLinkItem;
}

PBWeilaMsg* weilapb_decodeGetMsgRsp(const PB_UINT8* pData, PB_UINT32 dataSize, PB_BOOL unreadMsgRsp) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBLinkList));
    PBFieldData               msgListFeild;

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBLinkList* pList;
        pWeilaMsg->contentSize = sizeof(PBLinkList);
        pList                  = (PBLinkList*)pWeilaMsg->pContent;
        weilapb_initDecDataItem(&msgListFeild, PB_DATA_TYPE_LINK_SUBMSG, 0, pList);
        if (unreadMsgRsp) {
            weilapb_setDecCallback(&msgListFeild, &serviceMessage.sessionMessage.rspGetUnreadMsg.msgList, PB_NULL);
        } else {
            weilapb_setDecCallback(&msgListFeild, &serviceMessage.sessionMessage.rspGetMsg.msgList, PB_NULL);
        }
        msgListFeild.decSubMsg = weilapb_decodeSubMsgData;

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            weilapb_addRecyclePointer(pWeilaMsg, RECYCLE_PTR_MSGDATA_LINK, pList);
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        weilapb_freeMsgDataLink(pList);
        weilapb_freePBLinkList(pList);
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

void* weilapb_decodeSubMsgSessionInfo(pb_istream_t* stream, const pb_field_t* field) {
    WL_Session_SessionInfo wlSessionSessionInfo = WL_Session_SessionInfo_init_zero;
    PBLinkItem*            pPbLinkItem          = PB_NULL;
    pPbLinkItem                                 = (PBLinkItem*)PB_ALLOC(sizeof(PBSessionInfo) + sizeof(PBLinkItem));

    if (pPbLinkItem) {
        pPbLinkItem->dataSize = sizeof(PBLinkItem);
        if (pb_decode(stream, WL_Session_SessionInfo_fields, &wlSessionSessionInfo)) {
            PBSessionInfo* pSessionInfo = (PBSessionInfo*)pPbLinkItem->data;
            if (wlSessionSessionInfo.has_readMsgId) {
                pSessionInfo->readMsgId = wlSessionSessionInfo.readMsgId;
            }

            if (wlSessionSessionInfo.has_sessionId) {
                pSessionInfo->sessionId = wlSessionSessionInfo.sessionId;
            }

            if (wlSessionSessionInfo.has_sessionType) {
                pSessionInfo->sessionType = wlSessionSessionInfo.sessionType;
            }
        } else {
            PB_FREE(pPbLinkItem);
            pPbLinkItem = PB_NULL;
        }
    }

    return pPbLinkItem;
}

PBWeilaMsg* weilapb_decodeGetSessionRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBGetSessionRsp));

    PBFieldData sessionListField;

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBGetSessionRsp* pGetSessionRsp;
        pWeilaMsg->contentSize = sizeof(PBGetSessionRsp);
        pGetSessionRsp         = (PBGetSessionRsp*)pWeilaMsg->pContent;
        weilapb_initDecDataItem(&sessionListField, PB_DATA_TYPE_LINK_SUBMSG, 0, &pGetSessionRsp->sessionList);
        weilapb_setDecCallback(
            &sessionListField, &serviceMessage.sessionMessage.rspGetSession.sessionInfoList, PB_NULL);
        sessionListField.decSubMsg = weilapb_decodeSubMsgSessionInfo;

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            weilapb_addRecyclePointer(pWeilaMsg, RECYCLE_PTR_LINK, &pGetSessionRsp->sessionList);
            return pWeilaMsg;
        }

        weilapb_freePBLinkList(&pGetSessionRsp->sessionList);
        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeBurstCtrlRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PB_UINT32));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        pWeilaMsg->contentSize = sizeof(PB_UINT32);
        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.sessionMessage.rspBurstControl.has_duration) {
                PB_UINT32* pDuration = (PB_UINT32*)pWeilaMsg->pContent;
                *pDuration           = serviceMessage.sessionMessage.rspBurstControl.duration;
            }

            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeBurstCtrlNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBBurstCtlNtf));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        pWeilaMsg->contentSize = sizeof(PBBurstCtlNtf);
        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            PBBurstCtlNtf* pBurstCtlNtf = (PBBurstCtlNtf*)pWeilaMsg->pContent;
            if (serviceMessage.sessionMessage.ntfBurstControl.has_sessionId) {
                pBurstCtlNtf->sessionId = serviceMessage.sessionMessage.ntfBurstControl.sessionId;
            }

            if (serviceMessage.sessionMessage.ntfBurstControl.has_sessionType) {
                pBurstCtlNtf->sessionType = serviceMessage.sessionMessage.ntfBurstControl.sessionType;
            }

            if (serviceMessage.sessionMessage.ntfBurstControl.has_burstType) {
                pBurstCtlNtf->burstType = serviceMessage.sessionMessage.ntfBurstControl.burstType;
            }

            if (serviceMessage.sessionMessage.ntfBurstControl.has_talkerId) {
                pBurstCtlNtf->talkerId = serviceMessage.sessionMessage.ntfBurstControl.talkerId;
            }

            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeMonitorCtrlNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBMonitorCtlNtf));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        pWeilaMsg->contentSize = sizeof(PBMonitorCtlNtf);
        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            PBMonitorCtlNtf* pMonitorCtlNtf = (PBMonitorCtlNtf*)pWeilaMsg->pContent;
            if (serviceMessage.sessionMessage.ntfMonitorControl.has_monitorId) {
                pMonitorCtlNtf->monitorId = serviceMessage.sessionMessage.ntfMonitorControl.monitorId;
            }

            if (serviceMessage.sessionMessage.ntfMonitorControl.has_status) {
                pMonitorCtlNtf->status = serviceMessage.sessionMessage.ntfMonitorControl.status;
            }

            if (serviceMessage.sessionMessage.ntfMonitorControl.has_duration) {
                pMonitorCtlNtf->duration = serviceMessage.sessionMessage.ntfMonitorControl.duration;
            }

            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeVoipInviteRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBVoipInviteRsp));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBVoipInviteRsp*    pPBRsp;
        WL_Common_UserInfo* pAttr;
        PBFieldData         fieldData[USER_COUNT];
        pWeilaMsg->contentSize = sizeof(PBVoipInviteRsp);

        pPBRsp = (PBVoipInviteRsp*)pWeilaMsg->pContent;
        pAttr  = &(serviceMessage.sessionMessage.rspVoipInvite.userInfo);
        weilapb_prepareUserInfoField(fieldData, pAttr, &pPBRsp->userInfo);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.has_sessionMessage && serviceMessage.sessionMessage.has_rspVoipInvite) {
                if (serviceMessage.sessionMessage.rspVoipInvite.has_userInfo) {
                    if (serviceMessage.sessionMessage.rspVoipInvite.userInfo.has_userId) {
                        pPBRsp->userInfo.userId = serviceMessage.sessionMessage.rspVoipInvite.userInfo.userId;
                    }
                    if (serviceMessage.sessionMessage.rspVoipInvite.userInfo.has_sex) {
                        pPBRsp->userInfo.sex = serviceMessage.sessionMessage.rspVoipInvite.userInfo.sex;
                    }
                }
            }
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeVoipInviteNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBVoipInviteNtf));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBVoipInviteNtf*    pPBNtf;
        PBFieldData         callIdField;
        WL_Common_UserInfo* pAttr;
        PBFieldData         fieldData[USER_COUNT];
        pWeilaMsg->contentSize = sizeof(PBVoipInviteNtf);

        pPBNtf = (PBVoipInviteNtf*)pWeilaMsg->pContent;
        pAttr  = &(serviceMessage.sessionMessage.ntfVoipInvite.userInfo);
        weilapb_prepareUserInfoField(fieldData, pAttr, &pPBNtf->userInfo);

        weilapb_initDecDataItem(&callIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_NORMAL_STRING_LEN, pPBNtf->callId);
        weilapb_setDecCallback(&callIdField, &serviceMessage.sessionMessage.ntfVoipInvite.callId, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.has_sessionMessage && serviceMessage.sessionMessage.has_ntfVoipInvite) {
                if (serviceMessage.sessionMessage.ntfVoipInvite.has_userInfo) {
                    if (serviceMessage.sessionMessage.ntfVoipInvite.userInfo.has_userId) {
                        pPBNtf->userInfo.userId = serviceMessage.sessionMessage.ntfVoipInvite.userInfo.userId;
                    }
                    if (serviceMessage.sessionMessage.ntfVoipInvite.userInfo.has_sex) {
                        pPBNtf->userInfo.sex = serviceMessage.sessionMessage.ntfVoipInvite.userInfo.sex;
                    }
                }
                pPBNtf->userId = serviceMessage.sessionMessage.ntfVoipInvite.userId;
            }
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeVoipAcceptNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBVoipAcceptNtf));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBVoipAcceptNtf*    pPBNtf;
        PBFieldData         callIdField;
        WL_Common_UserInfo* pAttr;
        PBFieldData         fieldData[USER_COUNT];
        pWeilaMsg->contentSize = sizeof(PBVoipAcceptNtf);

        pPBNtf = (PBVoipAcceptNtf*)pWeilaMsg->pContent;
        pAttr  = &(serviceMessage.sessionMessage.ntfVoipAccept.userInfo);
        weilapb_prepareUserInfoField(fieldData, pAttr, &pPBNtf->userInfo);

        weilapb_initDecDataItem(&callIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_NORMAL_STRING_LEN, pPBNtf->callId);
        weilapb_setDecCallback(&callIdField, &serviceMessage.sessionMessage.ntfVoipAccept.callId, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.has_sessionMessage && serviceMessage.sessionMessage.has_ntfVoipAccept) {
                if (serviceMessage.sessionMessage.ntfVoipAccept.has_userInfo) {
                    if (serviceMessage.sessionMessage.ntfVoipAccept.userInfo.has_userId) {
                        pPBNtf->userInfo.userId = serviceMessage.sessionMessage.ntfVoipAccept.userInfo.userId;
                    }
                    if (serviceMessage.sessionMessage.ntfVoipAccept.userInfo.has_sex) {
                        pPBNtf->userInfo.sex = serviceMessage.sessionMessage.ntfVoipAccept.userInfo.sex;
                    }
                }
                pPBNtf->userId = serviceMessage.sessionMessage.ntfVoipAccept.userId;
            }
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeVoipHangupNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBVoipHangupNtf));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBVoipHangupNtf* pPBNtf;
        PBFieldData      callIdField;
        PBFieldData      fieldData[USER_COUNT];
        pWeilaMsg->contentSize = sizeof(PBVoipHangupNtf);

        pPBNtf = (PBVoipHangupNtf*)pWeilaMsg->pContent;
        weilapb_initDecDataItem(&callIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_NORMAL_STRING_LEN, pPBNtf->callId);
        weilapb_setDecCallback(&callIdField, &serviceMessage.sessionMessage.ntfVoipHangup.callId, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.has_sessionMessage && serviceMessage.sessionMessage.has_ntfVoipHangup) {
                pPBNtf->userId = serviceMessage.sessionMessage.ntfVoipHangup.userId;
            }
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg*
    weilapb_decodeSessionMsg(PB_INT32 commandId, PB_INT32 commandType, const PB_UINT8* pData, PB_UINT32 dataSize) {
    PBWeilaMsg* pWeilaMsg = PB_NULL;

    switch (commandId) {
        case WL_Session_SessionCommandId_SESSION_COMMAND_MSGDATA: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeMsgDataRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeMsgDataNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_MSGDATA_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_GET_MSG: {
            pWeilaMsg = weilapb_decodeGetMsgRsp(pData, dataSize, false);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_GET_MSG_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_READ_MSG: {
            pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_READ_MSG_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_GET_SESSION: {
            pWeilaMsg = weilapb_decodeGetSessionRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_GET_SESSION_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_REMOVE_SESSION: {
            pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_REMOVE_SESSION_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_GET_UNREAD_MSG: {
            pWeilaMsg = weilapb_decodeGetMsgRsp(pData, dataSize, true);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_GET_UNREAD_MSG_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_BURST_CONTROL: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeBurstCtrlNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_BURST_CTRL_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_MONITOR_CONTROL: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeMonitorCtrlNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_MONITOR_CTRL_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_VOIP_INVITE: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeVoipInviteRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeVoipInviteNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_VOIP_INVITE_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_VOIP_ACCEPT: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeVoipAcceptNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_VOIP_ACCEPT_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_COMMAND_VOIP_HANGUP: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeVoipHangupNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_SESSION_VOIP_HANGUP_MSG;
            }
        } break;

        case WL_Session_SessionCommandId_SESSION_EMBEDDED_COMMAND_ATTACH_SESSION: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
                if (pWeilaMsg) {
                    pWeilaMsg->message = WL_SESSION_EMBEDDED_ATTACH_MSG;
                }
            }
        } break;
    }

    return pWeilaMsg;
}
